// 核心原则
// 设计接口时，应该考虑使用者的角度，预见可能的错误，并通过接口设计防止
// 这些错误发生。一个好的接口应该像一把钥匙，只能以正确的方式插入锁中。

// 防止误用的关键技术
// 1. 引入新类型增强类型安全
class Date
{
public:
    Date(int month, int day, int year); // 容易误用：参数顺序不明确
};

Date d(30, 3, 2025); // 是3月30日还是30月3日？

// 改进方法是引入新类型，使接口自我描述：
class Month
{
public:
    static Month Jan() { return Month(1); }
    static Month Feb() { return Month(2); }
    // ...其他月份...
private:
    explicit Month(int m) : val(m) {}
    int val;
};

class Date
{
public:
    Date(Month m, int day, int year);
};

Date d(Month::Mar(), 30, 2025); // 清晰明了

// 2. 限制类型上的操作
// 确保类型上的所有操作都有意义：
class Rational
{
public:
    Rational(int numerator = 0, int denominator = 1);

private:
    int n, d;
};

// 问题：允许分母为0
Rational r(5, 0); // 数学上无意义！

// 改进：添加运行时检查
class Rational
{
public:
    Rational(int numerator = 0, int denominator = 1)
    {
        if (denominator == 0)
            throw std::invalid_argument("Denominator cannot be zero");
        n = numerator;
        d = denominator;
    }

private:
    int n, d;
};

// 3. 限制值域
// 使用类型系统限制值的范围：
// 表示百分比的类
class Percentage
{
private:
    int value;

public:
    explicit Percentage(int v)
    {
        if (v < 0 || v > 100)
            throw std::out_of_range("Percentage must be between 0 and 100");
        value = v;
    }

    int getValue() const { return value; }
};

// 使用
Percentage p(75);  // 正确
Percentage q(200); // 错误：抛出异常

// 4. 使用智能指针管理资源
// 防止资源泄漏的误用：
// 容易误用的接口
Investment *createInvestment(); // 调用者必须记得删除

// 改进：返回智能指针
std::unique_ptr<Investment> createInvestment();

// 使用
auto pInv = createInvestment(); // 不需要手动删除

// 5. 保持一致性
// 遵循"最小惊讶原则"，让接口行为符合用户的预期：
// STL容器的一致性
std::vector<int> v;
v.size();  // 所有容器都有size()
v.empty(); // 所有容器都有empty()
v.begin(); // 所有容器都有begin()和end()
v.end();

// 6. 与内置类型行为一致
// 让自定义类型的行为与内置类型相似：
class Rational
{
public:
    Rational(int num = 0, int den = 1);

    // 提供+=等运算符
    Rational &operator+=(const Rational &rhs);
    // 其他运算符...
};

// 非成员函数实现+
Rational operator+(const Rational &lhs, const Rational &rhs)
{
    Rational result(lhs);
    return result += rhs;
}

// 使用方式与内置类型一致
Rational a(1, 2), b(1, 3);
Rational c = a + b; // 行为与int类似

// 实际例子：日期范围类
// 问题版本
class DateRange
{
public:
    DateRange(const Date &start, const Date &end);
    Date getStart() const;
    Date getEnd() const;
};

// 可能的误用
DateRange range(Date(2025, 5, 1), Date(2025, 3, 1)); // 结束日期在开始日期之前

// 改进版本
class DateRange
{
public:
    DateRange(const Date &start, const Date &end)
    {
        if (start > end)
        {
            throw std::logic_error("End date cannot be before start date");
        }
        startDate = start;
        endDate = end;
    }

    Date getStart() const { return startDate; }
    Date getEnd() const { return endDate; }

private:
    Date startDate;
    Date endDate;
};

// 或者更进一步，创建工厂函数
static DateRange createDateRange(const Date &start, const Date &end)
{
    if (start <= end)
    {
        return DateRange(start, end);
    }
    else
    {
        return DateRange(end, start); // 自动调整顺序
    }
}

// 防止误用的更多技术
// 1. 使用explicit防止隐式类型转换
class String
{
public:
    explicit String(int size); // 分配指定大小的字符串
    String(const char *p);     // 从C字符串构造
};

String s1 = 10;         // 错误：不允许隐式转换
String s2("Hello");     // 正确
String s3 = String(10); // 正确：显式转换

// 2. 删除危险操作
class CriticalDataHandle
{
public:
    CriticalDataHandle();

    // 防止拷贝
    CriticalDataHandle(const CriticalDataHandle &) = delete;
    CriticalDataHandle &operator=(const CriticalDataHandle &) = delete;

    // 其他操作...
};

// 3. 使用编译时断言
template <typename T>
class Container
{
public:
    // 确保T是可复制的
    static_assert(std::is_copy_constructible<T>::value,
                  "Container elements must be copy-constructible");

    // ...
};

// 接口设计检查清单
// 设计接口时，问自己以下问题：

// 1.接口是否自我描述？ 参数名称和类型是否清晰表达了意图？
// 2.接口是否符合用户预期？ 是否遵循了常见的命名和行为约定？
// 3.接口是否防止了常见错误？ 是否使用类型系统防止错误？
// 4.接口是否易于正确使用？ 正确使用是否比错误使用更简单？
// 5.接口是否完整？ 是否提供了用户需要的所有功能？
// 6.接口是否一致？ 相似的操作是否有相似的接口？

// 实际应用：设计一个温度类
// 不好的设计
class Temperature
{
public:
    Temperature(double t) : temp(t) {} // 是摄氏度还是华氏度？

    void setTemp(double t) { temp = t; }
    double getTemp() const { return temp; }

private:
    double temp;
};

// 好的设计
class Temperature
{
public:
    // 明确的工厂函数
    static Temperature fromCelsius(double celsius)
    {
        return Temperature(celsius);
    }

    static Temperature fromFahrenheit(double fahrenheit)
    {
        return Temperature((fahrenheit - 32) * 5 / 9);
    }

    double celsius() const { return temp; }
    double fahrenheit() const { return temp * 9 / 5 + 32; }

private:
    explicit Temperature(double celsius) : temp(celsius) {}
    double temp; // 内部统一使用摄氏度
};

// 使用
auto t1 = Temperature::fromCelsius(25);
auto t2 = Temperature::fromFahrenheit(77);

// 总结
// 设计易于正确使用且难以误用的接口是一项重要技能。
// 通过引入新类型、限制操作、保持一致性、利用编译器检查等技术，
// 可以创建更安全、更直观的接口。记住，好的接口应该引导用户做正确的事，
// 而不是依赖文档或注释来避免错误。

// 这种设计思想不仅适用于类接口，也适用于函数接口、API设计，
// 甚至整个库和框架的设计。通过预见并防止潜在的误用，
// 可以大大提高代码的质量和可维护性。
